package itsm.isperp.framework.data.mybatis.utils;

import itsm.isperp.framework.domain.BaseBean;
import itsm.isperp.framework.domain.IBean;
import itsm.isperp.framework.utils.HibernateUUIDGenerator;
import itsm.isperp.framework.utils.Pair;
import itsm.isperp.framework.utils.ReflectHelper;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Id;

import org.apache.commons.lang3.StringUtils;

/**
 * 增加、更新及删除的映射对象工具
 * 
 * @author lizx
 * @date 2013-2-15
 * @version V1.0
 */
public final class CUDMapperObjectUtils {

	private transient static Map<Class<? extends IBean>, Pair<String, List<String>>> columnMap = new HashMap<Class<? extends IBean>, Pair<String, List<String>>>();

	/**
	 * 计算一个类的字段列表
	 * 
	 * @param clazz
	 *            类{@link Class}
	 * @return 一个包含两个对象的Pair实体{@link Pair}
	 */
	public static final <T extends IBean> Pair<String, List<String>> caculationColumnList(
			Class<? extends IBean> clazz) {
		Pair<String, List<String>> result = new Pair<String, List<String>>();

		if (columnMap.containsKey(clazz))
			return result;
		String pk = null;

		List<String> columnList = new ArrayList<String>();
		List<Field> list = new ArrayList<Field>();

		ReflectHelper.getDeclaredFields(clazz, list);

		for (Field field : list) {

			if (field.isAnnotationPresent(Id.class)) {
				columnList.add(field.getName());
				pk = field.getName();
			} else if (field.isAnnotationPresent(Column.class))
				columnList.add(field.getName());

		}
		result.setLeftObject(pk);
		result.setRightObject(columnList);
		columnMap.put(clazz, result);
		return result;
	}

	/**
	 * 用于获取Insert的字段累加
	 * 
	 * @return
	 */
	public static final <T extends IBean> String getInsertColumnsName(
			Class<? extends IBean> clazz) {
		StringBuilder sb = new StringBuilder();

		Pair<String, List<String>> pair = columnMap.get(clazz);
		if (pair == null)
			pair = caculationColumnList(clazz);
		List<String> list = pair.getRightObject();

		int i = 0;
		for (String column : list) {
			if (StringUtils.isEmpty(column))
				continue;

			if (i++ != 0)
				sb.append(',');
			sb.append(itsm.isperp.framework.utils.StringUtils
					.underscoreForSql(column));
		}
		// /转为一个使用_分割的字段
		return sb.toString();
	}

	/**
	 * 用于获取Insert的字段映射累加
	 * 
	 * @return
	 */
	public static final <T extends IBean> Pair<String, String> getInsertColumnsDefine(
			T obj) {
		StringBuilder sb = new StringBuilder();
		Class<? extends IBean> clazz = obj.getClass();
		Pair<String, String> pairResult = new Pair<String, String>();
		Pair<String, List<String>> pair = columnMap.get(clazz);
		if (pair == null)
			pair = caculationColumnList(clazz);
		List<String> list = pair.getRightObject();
		String id = HibernateUUIDGenerator.generate();

		int i = 0;
		for (String column : list) {
			if (StringUtils.isEmpty(column))
				continue;

			if (i++ != 0)
				sb.append(',');

			sb.append("#{").append(column).append('}');

		}
		pairResult.setLeftObject(id);
		pairResult.setRightObject(sb.toString());

		// 如果在保存前没有为id复制，则使用自动产生的uuid---此复制改由服务层进行操作
		// if (StringUtils.isEmpty(obj.getId()))
		// pairResult.getRightObject().replace(
		// ("#{" + pair.getLeftObject() + "}"), "'" + id + "'");

		return pairResult;
	}

	public static final <T extends IBean> Pair<String, String> getDynamicInsertSetFromSpecificColumns(
			T entity, Set<String> specificColumns) {

		StringBuilder sbl = new StringBuilder();
		StringBuilder sbr = new StringBuilder();

		Class<? extends IBean> clazz = entity.getClass();

		Pair<String, String> pairResult = new Pair<String, String>();

		Pair<String, List<String>> pair = columnMap.get(clazz);
		if (pair == null)
			pair = caculationColumnList(clazz);
		List<String> list = pair.getRightObject();

		int i = 0;

		if (!specificColumns.contains("id")) {
			sbl.append("id");
			sbr.append("#{param1.id}");
			i++;
		}

		for (String column : list) {
			if (StringUtils.isEmpty(column)
					|| !(specificColumns.contains(column) || specificColumns
							.contains(itsm.isperp.framework.utils.StringUtils
									.underscore(column))))
				continue;

			if (i++ != 0) {
				sbl.append(',');
				sbr.append(',');
			}
			sbl.append(itsm.isperp.framework.utils.StringUtils
					.underscoreForSql(column));

			sbr.append("#{param1.").append(column).append('}');

		}

		if (entity instanceof BaseBean) {

			if (!specificColumns.contains("creator")) {
				sbl.append(",").append(
						itsm.isperp.framework.utils.StringUtils
								.underscoreForSql("creator"));

				sbr.append(",").append("#{param1.").append("creator")
						.append('}');
			}
			if (!specificColumns.contains("modifier")) {
				sbl.append(",").append(
						itsm.isperp.framework.utils.StringUtils
								.underscoreForSql("modifier"));

				sbr.append(",").append("#{param1.").append("modifier")
						.append('}');
			}
			if (!specificColumns.contains("modifyDate")
					&& !specificColumns.contains("modify_date")) {
				sbl.append(",").append(
						itsm.isperp.framework.utils.StringUtils
								.underscoreForSql("modifyDate"));

				sbr.append(",").append("#{param1.").append("modifyDate")
						.append('}');
			}

		}

		pairResult.setLeftObject(sbl.toString());
		pairResult.setRightObject(sbr.toString());
		return pairResult;
	}

	/**
	 * 用于获取Update Set的字段累加
	 * 
	 * @return
	 */
	public static final <T extends IBean> String getUpdateSet(
			Class<? extends IBean> clazz, String prefix) {

		StringBuilder sb = new StringBuilder();
		Pair<String, List<String>> pair = columnMap.get(clazz);
		if (pair == null)
			pair = caculationColumnList(clazz);
		List<String> list = pair.getRightObject();

		int i = 0;
		for (String column : list) {
			if (StringUtils.isEmpty(column))
				continue;

			if (!column.equals(pair.getLeftObject())) {
				if (i++ != 0)
					sb.append(',');
				sb.append(
						itsm.isperp.framework.utils.StringUtils
								.underscoreForSql(column)).append("=#{")
						.append(prefix).append(column).append('}');
			}
		}

		return sb.toString();
	}

	/**
	 * 用于获取Update Set的字段累加
	 * 
	 * @return 动态更新实体的sql
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @throws NoSuchFieldException
	 * @throws SecurityException
	 */
	public static final <T extends IBean> String getDynamicUpdateSet(T entity,
			Map<String, Object> rules) throws SecurityException,
			NoSuchFieldException, IllegalArgumentException,
			IllegalAccessException {

		StringBuilder sb = new StringBuilder();
		Class<? extends IBean> clazz = entity.getClass();

		Pair<String, List<String>> pair = columnMap.get(clazz);
		if (pair == null)
			pair = caculationColumnList(clazz);
		List<String> list = pair.getRightObject();

		int i = 0;

		boolean haveRules = (rules != null);
		for (String column : list) {
			if (StringUtils.isEmpty(column))
				continue;

			Object columnValue = ReflectHelper.getValueByFieldName(entity,
					column);
			// 判断是否包含规则以及规则中包含指定的key
			if (!column.equals(pair.getLeftObject())
					&& (null != columnValue || (haveRules
							&& (rules.containsKey(column)) || rules
								.containsKey(itsm.isperp.framework.utils.StringUtils
										.underscore(column))))) {
				if (i++ != 0)
					sb.append(',');
				sb.append(
						itsm.isperp.framework.utils.StringUtils
								.underscoreForSql(column)).append("=#{param1.")
						.append(column).append('}');
			}
		}
		// System.out.println(sb);
		return sb.toString();
	}

	public static final <T extends IBean> String getDynamicUpdateSetBetweenTowEntity(
			T entity, T oldEntity) throws SecurityException,
			NoSuchFieldException, IllegalArgumentException,
			IllegalAccessException {

		StringBuilder sb = new StringBuilder();
		Class<? extends IBean> clazz = entity.getClass();

		Pair<String, List<String>> pair = columnMap.get(clazz);
		if (pair == null)
			pair = caculationColumnList(clazz);
		List<String> list = pair.getRightObject();

		int i = 0;

		for (String column : list) {
			if (StringUtils.isEmpty(column))
				continue;

			Object columnValue = ReflectHelper.getValueByFieldName(entity,
					column);

			Object oldEntityColumnValue = ReflectHelper.getValueByFieldName(
					oldEntity, column);

			if (!column.equals(pair.getLeftObject())) {

				if ((columnValue != null && !columnValue
						.equals(oldEntityColumnValue))
						|| (oldEntityColumnValue != null && !oldEntityColumnValue
								.equals(columnValue))) {

					if (i++ != 0)
						sb.append(',');
					sb.append(
							itsm.isperp.framework.utils.StringUtils
									.underscoreForSql(column))
							.append("=#{param1.").append(column).append('}');
				}
			}
		}
		if (i == 0) {

			return null;
		}
		// System.out.println(sb);
		return sb.toString();
	}

	public static final <T extends IBean> String getDynamicUpdateSetFromSpecificColumns(
			T entity, Set<String> specificColumns, boolean isInclude)
			throws SecurityException, NoSuchFieldException,
			IllegalArgumentException, IllegalAccessException {

		StringBuilder sb = new StringBuilder();
		Class<? extends IBean> clazz = entity.getClass();

		Pair<String, List<String>> pair = columnMap.get(clazz);
		if (pair == null)
			pair = caculationColumnList(clazz);
		List<String> list = pair.getRightObject();

		int i = 0;

		for (String column : list) {
			if (StringUtils.isEmpty(column))
				continue;
			boolean b = false;

			if (isInclude) {
				b = (specificColumns.contains(column) || specificColumns
						.contains(itsm.isperp.framework.utils.StringUtils
								.underscore(column)));
			} else {
				b = !(specificColumns.contains(column) || specificColumns
						.contains(itsm.isperp.framework.utils.StringUtils
								.underscore(column)));
			}

			if (b && !column.equals(pair.getLeftObject())) {

				if (i++ != 0)
					sb.append(',');
				sb.append(
						itsm.isperp.framework.utils.StringUtils
								.underscoreForSql(column)).append("=#{param1.")
						.append(column).append('}');
			}
		}

		if (i == 0) {

			return null;
		}
		// System.out.println(sb);
		return sb.toString();
	}

	public static final <T extends IBean> String getPrimaryKeyName(
			Class<? extends IBean> clazz) {
		Pair<String, List<String>> pair = columnMap.get(clazz);
		if (pair == null)
			pair = caculationColumnList(clazz);
		return pair.getLeftObject();
	}

}
